//! Function structures.

use stencil_tysys::ValueType;
use stencil_util::FunctionName;

use super::MirBlock;

/// A function to be generated within a module.
///
/// This might be a member function on a module, see `sig`.
#[derive(Clone, Debug)]
pub struct MirFunction {
    /// The unlocalized name of the function.
    name: FunctionName,

    /// The signature of the function.
    ///
    /// These types might end up getting erased, but we track them to inform
    /// generation.
    sig: MirSignature,

    /// What kind of function this is.
    kind: FnKind,

    /// Root code block.
    root_block: MirBlock,
}

impl MirFunction {
    pub fn new(name: FunctionName, sig: MirSignature, kind: FnKind, root_block: MirBlock) -> Self {
        Self {
            name,
            sig,
            kind,
            root_block,
        }
    }

    pub fn name(&self) -> &FunctionName {
        &self.name
    }

    pub fn sig(&self) -> &MirSignature {
        &self.sig
    }

    pub fn kind(&self) -> FnKind {
        self.kind
    }

    pub fn root_block(&self) -> &MirBlock {
        &self.root_block
    }

    pub fn arg_tys(&self) -> &[ValueType] {
        self.sig().arg_tys()
    }

    pub fn ret_ty(&self) -> Option<&ValueType> {
        self.sig().ret_ty()
    }

    pub fn is_instance_fn(&self) -> bool {
        self.sig().is_instance()
    }
}

/// Describes a function's signature.
#[derive(Clone, Debug)]
pub struct MirSignature {
    /// The arguments to the function.
    arg_tys: Vec<ValueType>,

    /// The return type of the function.
    ret_ty: Option<ValueType>,

    // Flags for special purposes.
    //
    // Should these be here?
    /// If it's an instance function.
    is_instance: bool,

    /// If it's a constructor.  Should not be an instance fn.
    is_constructor: bool,
}

impl MirSignature {
    pub fn new(
        arg_tys: Vec<ValueType>,
        ret_ty: Option<ValueType>,
        is_instance: bool,
        is_constructor: bool,
    ) -> Self {
        Self {
            arg_tys,
            ret_ty,
            is_instance,
            is_constructor,
        }
    }

    /// Constructs a signature for a primitive getter.
    pub fn new_prim_getter(ty: ValueType) -> Self {
        Self::new(Vec::new(), Some(ty), true, false)
    }

    /// Constructs a signature for a primitive getter.
    pub fn new_prim_setter(ty: ValueType) -> Self {
        Self::new(vec![ty], None, true, false)
    }

    /// Constructs a signature for a constructor.
    pub fn new_constructor(tys: Vec<ValueType>) -> Self {
        Self::new(tys, None, false, true)
    }

    /// Gets a ref to arguments to the function.
    pub fn arg_tys(&self) -> &[ValueType] {
        &self.arg_tys
    }

    /// Gets a ref to the return value of the function.
    pub fn ret_ty(&self) -> Option<&ValueType> {
        self.ret_ty.as_ref()
    }

    pub fn is_instance(&self) -> bool {
        self.is_instance
    }

    pub fn is_constructor(&self) -> bool {
        self.is_constructor
    }
}

/// What "kind" of function it is.
///
/// This informs special generation behavior about how a function should be
/// treated.  For example, a language might override function names to suit the
/// local naming conventions.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum FnKind {
    /// Generic function.  Will not be treated specially.
    Generic,

    /// Constructs an instance of the type natively without special behavior.
    Constructor,

    /// Wrapper for a constructor that does something special and returns an
    /// instance.
    ///
    /// Depending on the language these could be treated as overloaded
    /// constructors or as static functions on the type that invoke the
    /// constructor and return an instance of the type normally.
    ConstructorAlias,

    /// Returns the value of a field, or a ref to it.
    Getter,

    /// Sets the value of a field.
    Setter,
}
